package com.hzlj.position.locate.service.impl;

import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.Pair;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.RandomUtil;
import com.fz.common.base.core.date.DateUtils;
import com.fz.common.base.dto.DeleteCommonDTO;
import com.fz.common.base.dto.PageParamDTO;
import com.fz.common.base.dto.QueryPageResultDTO;
import com.fz.common.base.exception.PubException;
import com.fz.common.dict.annotation.DictConvert;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.hzlj.position.config.common.dto.jzJbxxDeviceBind.JzJbxxDeviceBindSimpleQueryParamDTO;
import com.hzlj.position.config.common.enums.ReportType;
import com.hzlj.position.config.config.PositionConfig;
import com.hzlj.position.config.service.JzJbxxDeviceBindService;
import com.hzlj.position.config.service.NoticeConfigService;
import com.hzlj.position.locate.common.dto.reportCommon.ReportInspectionStatDTO;
import com.hzlj.position.locate.common.dto.reportInspectionAutoSetting.ReportInspectionAutoSettingAddDTO;
import com.hzlj.position.locate.common.dto.reportInspectionAutoSetting.ReportInspectionAutoSettingQueryParamDTO;
import com.hzlj.position.locate.common.dto.reportInspectionAutoSetting.ReportInspectionAutoSettingQueryResultDTO;
import com.hzlj.position.locate.common.dto.reportInspectionAutoSetting.ReportInspectionAutoSettingUpdateDTO;
import com.hzlj.position.locate.common.model.ReportInspectionAutoSetting;
import com.hzlj.position.locate.converter.ReportInspectionAutoSettingConverter;
import com.hzlj.position.locate.dao.ReportInspectionAutoSettingDao;
import com.hzlj.position.locate.dao.ReportInspectionDao;
import com.hzlj.position.locate.service.ReportInspectionAutoSettingService;
import com.hzlj.position.locate.service.ReportSyncService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

import static com.hzlj.position.config.config.IdConfig.POSITION_ID_GEN;

/**
 * 通信联络核查-核查-自动配置表(ReportInspectionAutoSetting)表服务接口
 *
 * @author lifh
 * @date 2023-03-13 19:10:14
 */
@Slf4j
@Service
public class ReportInspectionAutoSettingServiceImpl implements ReportInspectionAutoSettingService {
    @Resource
    private ReportInspectionAutoSettingDao reportInspectionAutoSettingDao;
    @Resource
    private ReportInspectionDao reportInspectionDao;
    @Resource
    private ReportSyncService reportSyncService;
    @Resource
    private JzJbxxDeviceBindService jzJbxxDeviceBindService;
    @Resource
    private NoticeConfigService noticeConfigService;
    @Resource
    private PositionConfig positionConfig;

    /**
     * 通过id查询单条数据
     */
    @Override
    public ReportInspectionAutoSettingQueryResultDTO getReportInspectionAutoSettingById(String id) {
        ReportInspectionAutoSetting reportInspectionAutoSetting = this.reportInspectionAutoSettingDao.getById(id);
        if (Objects.isNull(reportInspectionAutoSetting)) {
            throw new PubException("自动任务不存在");
        }
        return ReportInspectionAutoSettingConverter.from(reportInspectionAutoSetting);
    }

    /**
     * 新增数据
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveReportInspectionAutoSetting(ReportInspectionAutoSettingAddDTO dto) {
        List<ReportInspectionAutoSetting> adds = dto.getJgs().stream().map(e -> {
            ReportInspectionAutoSetting add = ReportInspectionAutoSettingConverter.from(dto);
            add.setJgCode(e.getJgCode());
            add.setJgMc(e.getJgMc());
            add.setId(POSITION_ID_GEN.nextIdStr());
            return add;
        }).collect(Collectors.toList());

        for (ReportInspectionAutoSetting add : adds) {
            this.checkRepeat(add);
        }

        reportInspectionAutoSettingDao.saveOrUpdateBatch(adds);
        log.info("【新增数据成功】:reportInspectionAutoSetting={}", adds);
    }

    /**
     * 删除数据
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean deleteReportInspectionAutoSetting(DeleteCommonDTO<String> dto) {
        boolean result = this.reportInspectionAutoSettingDao.deleteReportInspectionAutoSetting(dto);
        if (result) {
            log.info("【删除数据成功】:reportInspectionAutoSetting={}", dto);
        }
        return result;
    }

    /**
     * 修改数据
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updateReportInspectionAutoSetting(ReportInspectionAutoSettingUpdateDTO dto) {
        //只能修改未开始的
        ReportInspectionAutoSetting exists = this.reportInspectionAutoSettingDao.getById(dto.getId());
        if (ObjectUtil.isEmpty(exists)) {
            throw new PubException("要修改的任务不存在");
        }
        Date startDay = exists.getStartDay();
        Date now = new Date();
        //如果是没有配置时间，则不认为是已经开始
        if (ObjectUtil.isNotEmpty(startDay)
                && DateUtil.beginOfDay(startDay).compareTo(DateUtil.beginOfDay(now)) <= 0) {
            throw new PubException("只能修改未开始的任务");
        }

        ReportInspectionAutoSetting update = ReportInspectionAutoSettingConverter.from(dto);
        this.checkRepeat(update);
        return this.reportInspectionAutoSettingDao.updateReportInspectionAutoSetting(update);
    }


    public boolean isOverTime(Date day, String endTime, Integer maxDelayTime) {
        Date settingEndTime = DateUtils.parseFixedHHmm(day, endTime);
        return DateUtil.between(settingEndTime, day, DateUnit.MINUTE, false) > maxDelayTime;
    }

    /**
     * 查询列表：分页
     */
    @Override
    @DictConvert
    public QueryPageResultDTO<ReportInspectionAutoSettingQueryResultDTO> pageReportInspectionAutoSetting(ReportInspectionAutoSettingQueryParamDTO dto, PageParamDTO page) {
        QueryPageResultDTO<ReportInspectionAutoSettingQueryResultDTO> result = this.reportInspectionAutoSettingDao.pageReportInspectionAutoSetting(dto, page);
        this.fillReportInspectionAutoResult(result.getList());
        return result;
    }

    @Override
    public Map<String, ReportInspectionAutoSettingQueryResultDTO> mostMatchedSetting4Sync(Date now) {
        List<ReportInspectionAutoSettingQueryResultDTO> settings = this.mostMatchedSetting4Sync(null, now);
        if (ObjectUtil.isEmpty(settings)) {
            return Maps.newHashMap();
        }

        //当前的时间
        String endTime = DateUtil.format(now, "HH:mm");
        //排除结束时间小于现在的
        return settings.stream()
                .filter(e -> (ObjectUtil.isEmpty(e.getEndTime()) || e.getEndTime().compareTo(endTime) >= 0))
                .collect(Collectors.toMap(ReportInspectionAutoSettingQueryResultDTO::getJgCode, e -> e, (e1, e2) -> e1, Maps::newTreeMap));
    }

    @Override
    public List<ReportInspectionAutoSettingQueryResultDTO> mostMatchedSetting4Sync(String jgCode, Date date) {
        //包含了当前有效期的，或者有效期为空的
        List<ReportInspectionAutoSetting> settings = reportInspectionAutoSettingDao.mostMatchedSetting4Sync(jgCode, date);
        if (ObjectUtil.isEmpty(settings)) {
            return Lists.newArrayList();
        }
        return settings.stream().map(ReportInspectionAutoSettingConverter::from).collect(Collectors.toList());
    }

    @Override
    public List<Pair<String, ReportInspectionAutoSettingQueryResultDTO>> randomJzryBySetting(Date now) {
        Date belongDay = DateUtil.beginOfDay(now);
        ReportType reportType = ReportType.AUTO;
        //取出setting缓存
        Map<String, ReportInspectionAutoSettingQueryResultDTO> settingCache = this.mostMatchedSetting4Sync(now);
        //取出同步配置
        Map<String, Date> syncLatestDayCache = reportSyncService.mapReportSync(reportType.getCode());//需要更新的Sync
        //转换:过滤+随机逻辑
        Map<String, List<String>> jzIdGroupByJgCode = this.randomJzIdsByJgCode(settingCache);
        //写入数据
        List<Pair<String, ReportInspectionAutoSettingQueryResultDTO>> allReports = Lists.newArrayList();
        for (Map.Entry<String, List<String>> entry : jzIdGroupByJgCode.entrySet()) {
            String jgCode = entry.getKey();
            //如果已经同步，则不需要重复同步
            Date latestDay = syncLatestDayCache.get(jgCode);
            if (latestDay != null && latestDay.compareTo(belongDay) >= 0) {
                log.info("【核查同步数据】{}，当前天已经处理：jgCode={},latestDay={}", reportType.getText(), jgCode, latestDay);
                continue;
            }
            List<Pair<String, ReportInspectionAutoSettingQueryResultDTO>> reports = entry.getValue().stream()
                    .map(jzId -> new Pair<>(jzId, settingCache.get(jgCode)))
                    .collect(Collectors.toList());
            //生成核验对象
            allReports.addAll(reports);
            log.info("【自动核验】 同步数据成功：jgCode={},size={}", jgCode, reports.size());
        }
        return allReports;
    }


    /**
     * 随机核查需核验人员
     * key=setting中的jgCode
     * value=jzId
     */
    private Map<String, List<String>> randomJzIdsByJgCode(Map<String, ReportInspectionAutoSettingQueryResultDTO> settingCache) {

        //转换
        Map<String, List<String>> result = Maps.newHashMap();
        settingCache.forEach((jgCode, setting) -> {
            JzJbxxDeviceBindSimpleQueryParamDTO params = jzJbxxDeviceBindService.getQueryParam4App()
                    .setMkh(!positionConfig.getInspection().getExcludeMkh())
                    .setQzcs(!positionConfig.getInspection().getExcludeQzcs());
            params.setJgCode(jgCode);
            List<String> jzIds = jzJbxxDeviceBindService.listJzIds(params);
            if (ObjectUtil.isEmpty(jzIds)) {
                return;
            }
            //需要核查的人数
            int checkNum = BigDecimal.valueOf(setting.getCheckRate())
                    .multiply(BigDecimal.valueOf(jzIds.size()))
                    .divide(BigDecimal.valueOf(100), RoundingMode.CEILING)
                    .setScale(0, RoundingMode.UP).intValue();
            if (checkNum <= 0) {
                return;
            }
            result.put(jgCode,
                    RandomUtil.randomEleList(jzIds, checkNum)
            );
        });

        return result;
    }

    private void checkRepeat(ReportInspectionAutoSetting setting) {
        String jgCode = setting.getJgCode();
        Date startDay = setting.getStartDay();
        Date endDay = setting.getEndDay();
        List<ReportInspectionAutoSetting> settings = reportInspectionAutoSettingDao.getReportInspectionAutoSettingByJgCode(jgCode);
        if (ObjectUtil.isEmpty(settings)) {
            return;
        }
        for (ReportInspectionAutoSetting oldSetting : settings) {
            if (oldSetting.getId().equals(setting.getId())) {
                continue;
            }
            Date oldStartDay = oldSetting.getStartDay();
            Date oldEndDay = oldSetting.getEndDay();
            //都为空
            if (ObjectUtil.isEmpty(startDay) && ObjectUtil.isEmpty(oldStartDay)) {
                throw new PubException("已经存在日期为空的点验配置");
            }
            //都不为空
            if (ObjectUtil.isNotEmpty(startDay) && ObjectUtil.isNotEmpty(oldStartDay)) {
                if (DateUtil.isOverlap(startDay, endDay, oldStartDay, oldEndDay)) {
                    throw new PubException("配置的开始、结束日期存在重叠");
                }
            }
        }
    }


    private void fillReportInspectionAutoResult(List<ReportInspectionAutoSettingQueryResultDTO> list) {
        String msg = "点验人数(%d)人，已上报(%d)人，未上报(%d)人，核查失败(%d)人，未核查(%d)人";
        Date now = new Date();
        String reportType = ReportType.AUTO.getCode();
        for (ReportInspectionAutoSettingQueryResultDTO setting : list) {
            Date startTime = DateUtils.parseFixedHHmm(setting.getStartDay(), setting.getStartTime());
            Date endTime = DateUtils.parseFixedHHmm(setting.getEndDay(), setting.getEndTime());
            //还没到开始天、或者还没到开始时间
            if (now.compareTo(startTime) < 0) {
                setting.setReportInspectionAutoResult("未开始");
                continue;
            }

            //统计点验结果
            ReportInspectionStatDTO stat;
            if (now.compareTo(endTime) > 0) {
                Date endDay = DateUtil.beginOfDay(endTime);
                stat = this.reportInspectionDao.statByReportStatus(setting.getJgCode(), reportType, endDay);
                //如果不是同一天
                if (!DateUtil.isSameDay(now, endTime)) {
                    setting.setReportInspectionAutoResult("已完成");
                } else {
                    int maxDelayMinutes = noticeConfigService.reportAutoDelayMinutes(setting.getJgCode());
                    boolean overTime = isOverTime(now, setting.getEndTime(), maxDelayMinutes);
                    setting.setReportInspectionAutoResult(overTime ? "已完成" : "点验中");
                }
            } else {
                stat = this.reportInspectionDao.statByReportStatus(setting.getJgCode(), reportType, DateUtil.beginOfDay(now));
                setting.setReportInspectionAutoResult("点验中");
            }

            //有统计数据
            setting.setReportInspectionAutoResultText(
                    String.format(msg, stat.getInspectionAmt(), stat.getInspectionSuccessAmt(), stat.getInspectionNeverAmt(), stat.getInspectionNoticeFailAmt(), stat.getInspectionNoticeNoneAmt())
            );
        }
    }

}
